import os
import pandas as pd
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import re
import threading


class MaterialChecker:
    def __init__(self, root):
        self.root = root
        self.root.title("学位申请材料检查工具")
        self.root.geometry("1400x950")

        # 数据存储
        self.df = None
        self.excel_path = ""
        self.folder_path = ""
        self.results = []
        self.is_checking = False

        self.setup_ui()

    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 标题
        title_label = ttk.Label(main_frame, text="国家开放大学学士学位申请材料检查",
                                font=("Arial", 16, "bold"))
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))

        # Excel文件选择
        ttk.Label(main_frame, text="选择Excel文件:").grid(row=1, column=0, pady=5, sticky=tk.W)
        ttk.Button(main_frame, text="选择Excel",
                   command=self.select_excel).grid(row=1, column=1, pady=5, sticky=tk.W)

        self.excel_label = ttk.Label(main_frame, text="未选择Excel文件", foreground="red")
        self.excel_label.grid(row=1, column=2, pady=5, sticky=tk.W)

        # 选择文件夹按钮
        ttk.Label(main_frame, text="选择材料文件夹:").grid(row=2, column=0, pady=5, sticky=tk.W)
        ttk.Button(main_frame, text="选择材料文件夹",
                   command=self.select_folder).grid(row=2, column=1, pady=5, sticky=tk.W)

        self.folder_label = ttk.Label(main_frame, text="未选择文件夹", foreground="red")
        self.folder_label.grid(row=2, column=2, pady=5, sticky=tk.W)

        # 进度条
        self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
        self.progress.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)

        # 检查按钮
        self.check_button = ttk.Button(main_frame, text="开始检查",
                                       command=self.start_check_thread)
        self.check_button.grid(row=4, column=0, pady=10, sticky=tk.W)

        # 结果显示区域
        result_frame = ttk.LabelFrame(main_frame, text="检查结果", padding="5")
        result_frame.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)

        # 创建树形视图显示结果
        columns = ("学号", "姓名", "申报次数", "论文", "评审表", "修改说明", "状态", "缺失材料")
        self.tree = ttk.Treeview(result_frame, columns=columns, show="headings", height=20)

        # 设置列标题和宽度
        column_widths = {
            "学号": 120,
            "姓名": 80,
            "申报次数": 80,
            "论文": 60,
            "评审表": 60,
            "修改说明": 80,
            "状态": 100,
            "缺失材料": 200
        }

        for col in columns:
            self.tree.heading(col, text=col)
            self.tree.column(col, width=column_widths.get(col, 100))

        # 滚动条
        scrollbar = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=self.tree.yview)
        self.tree.configure(yscrollcommand=scrollbar.set)

        self.tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        # 统计信息
        self.stats_label = ttk.Label(main_frame, text="", font=("Arial", 10, "bold"))
        self.stats_label.grid(row=6, column=0, columnspan=3, pady=5)

        # 按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=7, column=0, columnspan=3, pady=10)

        # 导出按钮
        ttk.Button(button_frame, text="导出检查结果",
                   command=self.export_results).grid(row=0, column=0, padx=5)

        # 清空结果按钮
        ttk.Button(button_frame, text="清空结果",
                   command=self.clear_results).grid(row=0, column=1, padx=5)

        # 查看详情按钮
        ttk.Button(button_frame, text="查看详情",
                   command=self.show_details).grid(row=0, column=2, padx=5)

        # 配置网格权重
        main_frame.columnconfigure(2, weight=1)
        main_frame.rowconfigure(5, weight=1)
        result_frame.columnconfigure(0, weight=1)
        result_frame.rowconfigure(0, weight=1)

        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)
        status_bar.grid(row=8, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)

    def select_excel(self):
        """选择Excel文件"""
        file_path = filedialog.askopenfilename(
            title="选择学位申请信息汇总表",
            filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
        )

        if file_path:
            try:
                # 直接从第一行开始读取Excel文件
                self.df = pd.read_excel(file_path)

                # 清理列名 - 去除空白字符
                self.df.columns = [str(col).strip() for col in self.df.columns]

                # 检查必要的列是否存在
                required_columns = ['学号', '姓名', '专业名称', '第几次申报']
                missing_columns = [col for col in required_columns if col not in self.df.columns]

                if missing_columns:
                    messagebox.showerror("错误", f"Excel文件中缺少必要的列: {missing_columns}\n"
                                                 f"当前列名: {list(self.df.columns)}")
                    self.df = None
                    return

                # 处理学号和申报次数的数据类型
                self.fix_data_types()

                # 过滤掉空行
                self.df = self.df[self.df['学号'].notna() & (self.df['学号'] != 'nan')]

                self.excel_path = file_path
                self.excel_label.config(text=os.path.basename(file_path), foreground="green")
                self.status_var.set(f"已加载Excel文件，共{len(self.df)}名学生")

            except Exception as e:
                messagebox.showerror("错误", f"读取Excel文件失败: {e}")

    def fix_data_types(self):
        """修复数据类型，特别是学号和申报次数"""
        # 处理学号 - 确保是字符串且没有小数点
        if '学号' in self.df.columns:
            # 先转换为字符串，然后去除可能的小数点
            self.df['学号'] = self.df['学号'].apply(
                lambda x: str(int(float(x))) if pd.notna(x) and str(x).replace('.', '').isdigit() else str(x))

        # 处理申报次数 - 确保是整数
        if '第几次申报' in self.df.columns:
            # 转换为整数，然后转换为字符串
            self.df['第几次申报'] = self.df['第几次申报'].apply(
                lambda x: str(int(float(x))) if pd.notna(x) and str(x).replace('.', '').isdigit() else '1'
            )

        # 确保其他必要列是字符串类型
        for col in ['姓名', '专业名称']:
            if col in self.df.columns:
                self.df[col] = self.df[col].astype(str)

    def select_folder(self):
        """选择材料文件夹"""
        folder_path = filedialog.askdirectory(title="选择包含学生材料的文件夹")
        if folder_path:
            self.folder_path = folder_path
            self.folder_label.config(text=folder_path, foreground="green")
            self.status_var.set(f"已选择材料文件夹: {folder_path}")

    def start_check_thread(self):
        """在新线程中开始检查"""
        if self.df is None:
            messagebox.showerror("错误", "请先选择Excel文件")
            return

        if not self.folder_path:
            messagebox.showerror("错误", "请先选择材料文件夹")
            return

        if self.is_checking:
            return

        # 禁用检查按钮
        self.check_button.config(state="disabled")
        self.is_checking = True

        # 启动进度条
        self.progress.start()
        self.status_var.set("正在检查材料...")

        # 在新线程中执行检查
        thread = threading.Thread(target=self.check_materials)
        thread.daemon = True
        thread.start()

    def check_materials(self):
        """检查材料"""
        try:
            # 获取文件夹中的所有文件
            all_files = os.listdir(self.folder_path)

            # 清空之前的结果
            self.results = []

            # 在UI线程中清空树形视图
            self.root.after(0, self.clear_tree)

            total_students = len(self.df)
            missing_count = 0

            # 检查每个学生的材料
            for index, row in self.df.iterrows():
                try:
                    # 获取学生信息 - 确保所有值都是字符串
                    student_id = self.safe_str(row.get('学号', ''))
                    student_name = self.safe_str(row.get('姓名', ''))
                    application_count = self.safe_str(row.get('第几次申报', '1'))
                    major = self.safe_str(row.get('专业名称', ''))

                    # 跳过无效行
                    if not student_id or student_id == 'nan' or not student_name or student_name == 'nan':
                        continue

                    # 检查论文评审表
                    review_pattern = f"论文评审表_{student_id}_{student_name}.pdf"
                    review_file = self.find_file(all_files, review_pattern)

                    # 检查论文（支持DOC和DOCX格式）
                    thesis_file = ""

                    # 尝试匹配精确的专业名称
                    if major and major != 'nan':
                        # 转义专业名称中的特殊字符
                        escaped_major = re.escape(major)

                        # 尝试DOC格式
                        thesis_pattern_doc = f"{student_id}_{student_name}_.*_{escaped_major}.doc"
                        thesis_file = self.find_file_by_pattern(all_files, thesis_pattern_doc)

                        # 如果DOC没找到，尝试DOCX格式
                        if not thesis_file:
                            thesis_pattern_docx = f"{student_id}_{student_name}_.*_{escaped_major}.docx"
                            thesis_file = self.find_file_by_pattern(all_files, thesis_pattern_docx)

                    # 如果精确匹配失败，尝试更宽松的匹配（不包含专业名称）
                    if not thesis_file:
                        thesis_pattern_doc = f"{student_id}_{student_name}_.*.doc"
                        thesis_file = self.find_file_by_pattern(all_files, thesis_pattern_doc)

                        if not thesis_file:
                            thesis_pattern_docx = f"{student_id}_{student_name}_.*.docx"
                            thesis_file = self.find_file_by_pattern(all_files, thesis_pattern_docx)

                    # 检查论文修改说明（申报次数>=2时需要）
                    revision_required = application_count.isdigit() and int(application_count) >= 2
                    revision_file = ""
                    if revision_required:
                        revision_pattern = f"论文修改说明_{student_id}_{student_name}.pdf"
                        revision_file = self.find_file(all_files, revision_pattern)

                    # 判断状态和缺失材料
                    status = "完整"
                    missing_materials = []

                    if not thesis_file:
                        missing_materials.append("论文")
                    if not review_file:
                        missing_materials.append("评审表")
                    if revision_required and not revision_file:
                        missing_materials.append("修改说明")

                    if missing_materials:
                        status = "缺失"
                        missing_count += 1

                    # 添加到结果列表
                    result = {
                        '学号': student_id,
                        '姓名': student_name,
                        '申报次数': application_count,
                        '论文': "✓" if thesis_file else "✗",
                        '评审表': "✓" if review_file else "✗",
                        '修改说明': "✓" if revision_file else ("不需要" if not revision_required else "✗"),
                        '状态': status,
                        '缺失材料': ", ".join(missing_materials) if missing_materials else "无",
                        '论文文件名': thesis_file if thesis_file else "未找到",
                        '评审表文件名': review_file if review_file else "未找到",
                        '修改说明文件名': revision_file if revision_file else (
                            "不需要" if not revision_required else "未找到")
                    }
                    self.results.append(result)

                    # 在UI线程中更新树形视图
                    self.root.after(0, self.add_tree_item, (
                        student_id, student_name, application_count,
                        "✓" if thesis_file else "✗",
                        "✓" if review_file else "✗",
                        "✓" if revision_file else ("不需要" if not revision_required else "✗"),
                        status,
                        ", ".join(missing_materials) if missing_materials else "无"
                    ))

                except Exception as e:
                    # 处理单个学生的错误，继续检查其他学生
                    error_msg = f"处理学生 {row.get('姓名', '未知')} 数据时出错: {e}"
                    print(error_msg)  # 在控制台打印错误信息
                    continue

            # 在UI线程中更新统计信息
            complete_count = total_students - missing_count
            self.root.after(0, self.update_stats, total_students, complete_count, missing_count)

        except Exception as e:
            # 在UI线程中显示错误
            self.root.after(0, lambda: messagebox.showerror("错误", f"检查过程中出错: {e}"))

        finally:
            # 在UI线程中恢复UI状态
            self.root.after(0, self.check_completed)

    def safe_str(self, value):
        """安全地将值转换为字符串"""
        if pd.isna(value):
            return ""
        return str(value).strip()

    def clear_tree(self):
        """清空树形视图"""
        for item in self.tree.get_children():
            self.tree.delete(item)

    def add_tree_item(self, values):
        """向树形视图添加项目"""
        self.tree.insert("", "end", values=values)

    def update_stats(self, total, complete, missing):
        """更新统计信息"""
        complete_rate = complete / total * 100 if total > 0 else 0
        self.stats_label.config(
            text=f"总计: {total}人 | 完整: {complete}人 | 缺失: {missing}人 | 完整率: {complete_rate:.1f}%"
        )

    def check_completed(self):
        """检查完成后的清理工作"""
        self.progress.stop()
        self.check_button.config(state="normal")
        self.is_checking = False
        self.status_var.set("检查完成")

    def find_file(self, file_list, pattern):
        """在文件列表中查找精确匹配的文件"""
        for file in file_list:
            if file == pattern:
                return file
        return ""

    def find_file_by_pattern(self, file_list, pattern):
        """使用正则表达式在文件列表中查找匹配的文件"""
        try:
            compiled_pattern = re.compile(pattern, re.IGNORECASE)
            for file in file_list:
                if compiled_pattern.search(file):
                    return file
        except re.error:
            # 如果正则表达式有错误，尝试简单的字符串匹配
            for file in file_list:
                if pattern.replace(".*", "") in file:
                    return file
        return ""

    def export_results(self):
        """导出检查结果到Excel"""
        if not self.results:
            messagebox.showwarning("警告", "没有可导出的结果")
            return

        output_file = filedialog.asksaveasfilename(
            defaultextension=".xlsx",
            filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")],
            title="保存检查结果"
        )

        if output_file:
            try:
                # 创建一个包含所有信息的DataFrame用于导出
                export_data = []
                for result in self.results:
                    export_data.append({
                        '学号': result['学号'],
                        '姓名': result['姓名'],
                        '申报次数': result['申报次数'],
                        '论文状态': result['论文'],
                        '评审表状态': result['评审表'],
                        '修改说明状态': result['修改说明'],
                        '总体状态': result['状态'],
                        '缺失材料': result['缺失材料'],
                        '论文文件名': result['论文文件名'],
                        '评审表文件名': result['评审表文件名'],
                        '修改说明文件名': result['修改说明文件名']
                    })

                result_df = pd.DataFrame(export_data)
                result_df.to_excel(output_file, index=False)
                messagebox.showinfo("成功", f"结果已导出到: {output_file}")
            except Exception as e:
                messagebox.showerror("错误", f"导出失败: {e}")

    def clear_results(self):
        """清空结果"""
        for item in self.tree.get_children():
            self.tree.delete(item)
        self.stats_label.config(text="")
        self.status_var.set("已清空结果")

    def show_details(self):
        """显示检查详情"""
        if not self.results:
            messagebox.showwarning("警告", "没有可查看的检查结果")
            return

        # 创建详情窗口
        details_window = tk.Toplevel(self.root)
        details_window.title("检查详情")
        details_window.geometry("800x600")

        # 创建文本框显示详情
        text_frame = ttk.Frame(details_window, padding="10")
        text_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        text_area = tk.Text(text_frame, wrap=tk.WORD, width=80, height=30)
        scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=text_area.yview)
        text_area.configure(yscrollcommand=scrollbar.set)

        text_area.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        # 添加详情内容
        details_text = "材料检查详情\n\n"
        details_text += f"检查时间: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
        details_text += f"Excel文件: {self.excel_path}\n"
        details_text += f"材料文件夹: {self.folder_path}\n\n"

        # 按状态分类统计
        status_counts = {}
        for result in self.results:
            status = result['状态']
            status_counts[status] = status_counts.get(status, 0) + 1

        details_text += "状态统计:\n"
        for status, count in status_counts.items():
            details_text += f"  {status}: {count}人\n"

        details_text += "\n详细结果:\n"
        for result in self.results:
            details_text += f"\n学号: {result['学号']}, 姓名: {result['姓名']}, 状态: {result['状态']}\n"
            details_text += f"  论文: {result['论文']} ({result['论文文件名']})\n"
            details_text += f"  评审表: {result['评审表']} ({result['评审表文件名']})\n"
            details_text += f"  修改说明: {result['修改说明']} ({result['修改说明文件名']})\n"
            if result['缺失材料'] != "无":
                details_text += f"  缺失材料: {result['缺失材料']}\n"

        text_area.insert(tk.END, details_text)
        text_area.config(state=tk.DISABLED)  # 设置为只读

        # 配置网格权重
        details_window.columnconfigure(0, weight=1)
        details_window.rowconfigure(0, weight=1)
        text_frame.columnconfigure(0, weight=1)
        text_frame.rowconfigure(0, weight=1)


def main():
    # 创建主窗口
    root = tk.Tk()
    app = MaterialChecker(root)
    root.mainloop()


if __name__ == "__main__":
    main()